package com.geeguo.ebuilder.operation.base.log.aspect;

import cn.hutool.core.util.ObjectUtil;
import com.geeguo.ebuilder.core.common.dictionary.BooleanEnum;
import com.geeguo.ebuilder.core.common.utils.GsonUtils;
import com.geeguo.ebuilder.core.common.utils.IpUtils;
import com.geeguo.ebuilder.core.common.utils.ServletUtils;
import com.geeguo.ebuilder.core.common.utils.key.UUIDGenerator;
import com.geeguo.ebuilder.operation.base.security.model.LoginUser;
import com.geeguo.ebuilder.operation.base.security.utils.AuthUtils;
import com.geeguo.ebuilder.operation.base.log.annotation.Log;
import com.geeguo.ebuilder.operation.base.log.service.LogRecordService;
import com.geeguo.ebuilder.operation.base.log.model.LogRecord;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.NamedThreadLocal;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.Map;

@Slf4j
@Aspect
@Component
public class LogAspect {

    @Autowired
    private LogRecordService logRecordService;

    /** 计算操作消耗时间 */
    private static final ThreadLocal<Long> REQUEST_START_TIME_THREADLOCAL = new NamedThreadLocal<Long>("Request Start Time");

    /**
     * 处理请求前执行
     */
    @Before(value = "@annotation(controllerLog)")
    public void doBefore(JoinPoint joinPoint, Log controllerLog) {
        REQUEST_START_TIME_THREADLOCAL.set(System.currentTimeMillis());
    }

    /**
     * 处理完请求后执行
     *
     * @param joinPoint 切点
     */
    @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "result")
    public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object result) {
        handleLog(joinPoint, controllerLog, null, result);
    }

    /**
     * 拦截异常操作
     *
     * @param joinPoint 切点
     * @param e         异常
     */
    @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) {
        handleLog(joinPoint, controllerLog, e, null);
    }

    protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception exception, Object result) {
        try {
            HttpServletRequest request = ServletUtils.getRequest();
            LoginUser loginUser = AuthUtils.getLoginUser();

            LogRecord logRecord = new LogRecord();
            logRecord.setId(UUIDGenerator.getId());
            // 设置标题
            logRecord.setTitle(controllerLog.title());
            // 设置业务类型
            logRecord.setLogType(controllerLog.logType().getValue());
            // 设置客户端类型
            logRecord.setClientType(controllerLog.clientType().getValue());
            // 设置方法名称
            String className = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            logRecord.setMethod(className + "." + methodName + "()");
            // 设置操作人员信息
            logRecord.setUserId(loginUser.getUserId());
            logRecord.setUsername(loginUser.getRealName());
            // 设置HTTP请求信息
            String ip = IpUtils.getIpAddr(request);
            logRecord.setIp(ip);
            logRecord.setUrl(StringUtils.substring(request.getRequestURI(), 0, 255));
            logRecord.setRequestMethod(request.getMethod());
            // 保存请求数据
            if (controllerLog.isSaveRequestData()) {
                setRequestParam(logRecord, request);
            }
            // 设置返回结果信息
            if (controllerLog.isSaveResponseData() && ObjectUtil.isNotEmpty(result)) {
                logRecord.setJsonResult(StringUtils.substring(GsonUtils.toJson(result), 0, 2000));
            }
            logRecord.setElapsedTime(System.currentTimeMillis() - REQUEST_START_TIME_THREADLOCAL.get());
            // 设置异常信息
            if (exception == null) {
                logRecord.setSuccess(BooleanEnum.True.ordinal());
            } else {
                logRecord.setSuccess(BooleanEnum.False.ordinal());
                logRecord.setErrorMsg(StringUtils.substring(exception.getMessage(), 0, 2000));
            }
            logRecord.setCreateTime(new Date());
            logRecord.setCreateUser(loginUser.getUserId());
            logRecordService.record(logRecord);
        } catch (Exception exp) {
            log.error("操作日志记录信息:{}", exp.getMessage());
        } finally {
            REQUEST_START_TIME_THREADLOCAL.remove();
        }
    }

    private void setRequestParam(LogRecord logRecord, HttpServletRequest request) {
        Map<?, ?> parameterMap = request.getParameterMap();
        if (MapUtils.isNotEmpty(parameterMap)) {
            logRecord.setRequestParam(StringUtils.substring(GsonUtils.toJson(parameterMap), 0, 2000));
        }
    }
}
